const MESSAGE_TYPE_ANSWER = 0x02;
const MESSAGE_TYPE_CANDIDATE = 0x03;
const MESSAGE_TYPE_HANGUP = 0x04;

const SIGNAL_TYPE_JOIN = "join";                // 创建房间 或者 加入到现有房间
const SIGNAL_TYPE_NEW_PEER = "new-peer";        // 成功加入到现有房间后，通知房间中的其他人谁加入了
const SIGNAL_TYPE_RESP_JOIN = "resp-join";      // 成功加入到现有房间后，通知自己房间中有哪些人
const SIGNAL_TYPE_OFFER = "offer";              // 发送offer给对端
const SIGNAL_TYPE_ANSWER = "answer";            // 发送answer给对端
const SIGNAL_TYPE_CANDIDATE = "candidate";      // 发送candidate给对端
const SIGNAL_TYPE_LEAVE = "leave";              // 告诉服务器自己要离开房间
const SIGNAL_TYPE_PEER_LEAVE = "peer-leave";    // 离开房间后，通知房间中的其他人

var WebSocket = require("ws");
var moment = require("moment");
var port = 8089;

/** 
 * rooms（房间）的主要逻辑
 * 1、数据结构：
 *  rooms:[
 *    {roomId:1, users:[{userId:1, obj:{...}}, userId:2, obj:{...}]},
 *    {roomId:2, users:[{userId:1, obj:{...}}, userId:2, obj:{...}]}
 *  ]
 * 2、rooms 可以理解成房间列表，结构为：[{roomId:1, users:map}]
 * 3、每个房间对应的用户列表，也是一个Map，结构为：[{userId:1, conn:conn}]
 * 4、目前只允许两个人一对一视频通话
 */
var rooms = new Map();

const wss = new WebSocket.Server({ port: port });
wss.on('connection', (ws, req) => {
    // 从url中解析roomId和userId
    let { url } = req;
    let [_, roomId, userId] = url.slice(0).split('/');
    ws.roomId = roomId;
    ws.userId = userId;
    console.log(`${moment().format("yyyy-MM-DD HH:mm:ss")} [websocket onconnection] roomId:${roomId}, userId:${userId}`);

    ws.on('message', (rawData) => {
        console.log(`${moment().format("yyyy-MM-DD HH:mm:ss")} [websocket onmessage] userId:${ws.userId}, rawDate:${rawData}`);

        let message = JSON.parse(rawData);

        switch (message.cmd) {
            case SIGNAL_TYPE_JOIN:
                handleJoin(message, ws);
                break;
            case SIGNAL_TYPE_LEAVE:
                handleLeave(message, ws);
                break;
            case SIGNAL_TYPE_OFFER:
                handleOffer(message, ws);
                break;
            case SIGNAL_TYPE_ANSWER:
                handleAnswer(message, ws);
                break;
            case SIGNAL_TYPE_CANDIDATE:
                handleCandidate(message, ws);
                break;
        }
    });

    ws.on('close', (code, reason) => {
        console.log(`${moment().format("yyyy-MM-DD HH:mm:ss")} [websocket onclose] code:${code}, reason:${reason}`);
    })

    ws.on("error", (error) => {
        console.err(`${moment().format("yyyy-MM-DD HH:mm:ss")} [websocket onerror] error:${error}`);
    });
});

function handleJoin(message, conn) {
    console.log(`-->handleJoin.`);

    let roomId = message.roomId;
    let userId = message.userId;

    // 房间ID在列表中不存在，就加进去
    if (!rooms.has(roomId)) {
        rooms.set(roomId, new Map());
    }

    // 根据房间ID取用户列表
    var users = rooms.get(roomId);

    // 目前只做到1对1通话，如果房间中的用户数量大于1，当前用户不能加入房间
    if (users.size > 1) {
        console.error(`-->handleJoin. room ${roomId} person reaching the upper limit.`);
        // todo: 提醒当前用户
        return;
    }

    // 用户ID在列表中不存在，就加进去
    if (!users.has(userId)) {
        users.set(userId, conn);
        // 自己刚好是第二个加入
        if (users.size == 2) {
            users.forEach((_conn, uid) => {
                if (uid != userId) {
                    // 告诉另一端“有人加入房间”
                    let jMsg = {
                        'cmd': SIGNAL_TYPE_NEW_PEER,
                        'remoteUserId': userId
                    }
                    let msg = JSON.stringify(jMsg);
                    _conn.send(msg);
                    console.log(`${moment().format("yyyy-MM-DD HH:mm:ss")} [websocket send] message:${msg}`)

                    // 告诉自己“已成功加入到某房间”
                    jMsg = {
                        'cmd': SIGNAL_TYPE_RESP_JOIN,
                        'remoteUserId': uid
                    }
                    msg = JSON.stringify(jMsg);
                    conn.send(msg);
                    console.log(`${moment().format("yyyy-MM-DD HH:mm:ss")} [websocket send] message:${msg}`)
                }
            });
        }
    }
}

function handleOffer(message, conn) {
    console.log(`-->handleOffer.`);

    let roomId = message.roomId;
    let userId = message.userId;
    let remoteUserId = message.remoteUserId;

    let users = rooms.get(roomId);
    if (users == null) {
        console.log(`-->handleOffer. can't get room by ${roomId}`);
        return;
    }

    let user = users.get(userId);
    if (user == null) {
        console.log(`-->handleOffer. can't get user by ${userId}`);
        return;
    }

    let remoteUser = users.get(remoteUserId);
    if (remoteUser == null) {
        console.log(`-->handleOffer. can't get remote user by ${remoteUserId}`);
        return;
    }

    // 把offer发给另一端
    var msg = JSON.stringify(message);
    remoteUser.send(msg);
    console.log(`${moment().format("yyyy-MM-DD HH:mm:ss")} [websocket send] message:${msg}`)
}

function handleAnswer(message, conn) {
    console.log(`-->handleAnswer.`);

    let roomId = message.roomId;
    let userId = message.userId;
    let remoteUserId = message.remoteUserId;

    let users = rooms.get(roomId);
    if (users == null) {
        console.log(`-->handleAnswer. can't get room by ${roomId}`);
        return;
    }

    let user = users.get(userId);
    if (user == null) {
        console.log(`-->handleAnswer. can't get user by ${userId}`);
        return;
    }

    let remoteUser = users.get(remoteUserId);
    if (remoteUser == null) {
        console.log(`-->handleAnswer. can't get remote user by ${remoteUserId}`);
        return;
    }

    // 把answer发给另一端
    var msg = JSON.stringify(message);
    remoteUser.send(msg);
    console.log(`${moment().format("yyyy-MM-DD HH:mm:ss")} [websocket send] message:${msg}`)
}

function handleCandidate(message, conn) {
    console.log(`-->handleCandidate.`);

    let roomId = message.roomId;
    let userId = message.userId;
    let remoteUserId = message.remoteUserId;

    let users = rooms.get(roomId);
    if (users == null) {
        console.log(`-->handleCandidate. can't get room by ${roomId}`);
        return;
    }

    let user = users.get(userId);
    if (user == null) {
        console.log(`-->handleCandidate. can't get user by ${userId}`);
        return;
    }

    let remoteUser = users.get(remoteUserId);
    if (remoteUser == null) {
        console.log(`-->handleCandidate. can't get remote user by ${remoteUserId}`);
        return;
    }

    // 把candidate发给另一端
    var msg = JSON.stringify(message);
    remoteUser.send(msg);
    console.log(`${moment().format("yyyy-MM-DD HH:mm:ss")} [websocket send] message:${msg}`)
}

function handleLeave(message, conn) {
    console.log(`-->handleLeave.`);

    let roomId = message.roomId;
    let userId = message.userId;

    let users = rooms.get(roomId);
    if (users == null) {
        console.log(`-->handleLeave. can't get room ${roomId}`);
        return;
    }
    let user = users.get(userId);
    if (user == null) {
        console.log(`-->handleLeave. can't get user ${userId}`);
        return;
    }

    users.delete(userId);

    // 通知房间里的其他人
    if (users.size > 0) {
        users.forEach((_conn, uid) => {
            let jMsg = {
                'cmd': SIGNAL_TYPE_PEER_LEAVE,
                'remoteUserId': userId
            };
            let msg = JSON.stringify(jMsg);
            _conn.send(msg);
            console.log(`${moment().format("yyyy-MM-DD HH:mm:ss")} [websocket send] message:${msg}`)
        });
    }
}

// function broadcast(message){
//     wss.clients.forEach((client) => {
//         if(client.readyState == WebSocket.OPEN){
//             client.send(message, (err) => {
//                 if(err){
//                     console.error(`websocket send message error: ${err}`);
//                 }
//             });
//         }
//     });
// }